package com.sysdep;

import java.util.ArrayList;
import java.util.List;
import com.asm.*;

public class CodeAssembly {
    public List<Assembly> assemblies;
    private SymbolTable labelSymbols;

    public CodeAssembly() {
        assemblies = new ArrayList<Assembly>();
        labelSymbols = new SymbolTable("L");
    }

    public List<Assembly> getAssemblies() {
        return assemblies;
    }

    public void addAll(CodeAssembly ca) {
        assemblies.addAll(ca.getAssemblies());
    }

    public void _data() {
        assemblies.add(new Directive(".data"));
    }

    public void _text() {
        assemblies.add(new Directive(".text"));
    }

    public void _globl(Symbol sym) {
        assemblies.add(new Directive(".global\t" + sym.name()));
    }

    public void _type(Symbol sym, LabelType type) {
        switch (type) {
            case obj:
                assemblies.add(new Directive("\t.type\t" + sym.name() + ",%object"));
                break;
            case func:
                assemblies.add(new Directive("\t.type\t" + sym.name() + ",%function"));
                break;
            default:// error
                return;
        }
    }

    public void _size(Symbol sym) {
        assemblies.add(new Directive("\t.size\t" + sym.name() + ",.-" + sym.name()));
    }

    public void _size(Symbol sym, long size) {
        assemblies.add(new Directive("\t.size\t" + sym.name() + "," + Long.toString(size)));
    }

    public void _int(long value) {
        assemblies.add(new Directive("\t.int\t" + Long.toString(value)));
    }

    public void _float(float value) {
        assemblies.add(new Directive("\t.float\t" + Float.toString(value)));
    }

    public void _word(String name) {
        assemblies.add(new Directive("\t.word\t" + name));
    }

    public void label(Symbol sym) {
        assemblies.add(new Label(sym));
    }

    public void label(Label label) {
        assemblies.add(label);
    }

    public void insn(String op, Operand dest, Operand src1, Operand src2) {
        assemblies.add(new Instruction(op, "", new Operand[] { dest, src1, src2 }, false));
    }

    public void insn(String op, String suffix, Operand dest, Operand src) {
        assemblies.add(new Instruction(op, suffix, dest, src));
    }

    public void insn(String op, Operand dest, Operand src) {
        insn(op, "", dest, src);
    }

    public void insn(String op, Operand src) {
        assemblies.add(new Instruction(op, "", src));
    }

    public void insn(String op) {
        assemblies.add(new Instruction(op));
    }

    public void sub(Register dest, Operand base, Operand diff) {
        insn("sub", dest, base, diff);
    }

    public void subne(Register dest, Operand base, Operand diff) {
        insn("subne", dest, base, diff);
    }

    public void sublt(Register dest, Operand base, Operand diff) {
        insn("sublt", dest, base, diff);
    }

    public void push(Operand src1, Operand src2) {
        insn("push", src1, src2);
    }

    public void push(Operand src1) {
        insn("push", src1);
    }

    public void pop(Operand dest1, Operand dest2) {
        insn("pop", dest1, dest2);
    }

    public void pop(Operand dest1) {
        insn("pop", dest1);
    }

    public void mov(Operand dest, Operand src) {
        insn("mov", dest, src);
    }

    public void moveq(Operand dest, Operand src) {
        insn("mov", "eq", dest, src);
    }
    public void movne(Operand dest, Operand src) {
        insn("mov", "ne", dest, src);
    }

    public void movgt(Operand dest, Operand src) {
        insn("movgt", dest, src);
    }

    public void movge(Operand dest, Operand src) {
        insn("movge", dest, src);
    }

    public void movlt(Operand dest, Operand src) {
        insn("movlt", dest, src);
    }

    public void movle(Operand dest, Operand src) {
        insn("movle", dest, src);
    }

    public void ldr(Operand dest, Operand src) {
        insn("ldr", dest, src);
    }

    public void str(Operand src, Operand dest) {
        insn("str", src, dest);
    }
    
    public void ret(Register reg) {
        insn("bx", reg);
    }

    public void jmp(Label label) {
        insn("b", new DirectMemoryReference(label.symbol()));
    }

    public void jmplt(Label label) {
        insn("blt", new DirectMemoryReference(label.symbol()));
    }

    public void jmpne(Label label) {
        insn("bne", new DirectMemoryReference(label.symbol()));
    }

    public void add(Operand dest, Operand src1, Operand src2) {
        insn("add", dest, src1, src2);
    }

    public void addge(Operand dest, Operand src1, Operand src2) {
        insn("addge", dest, src1, src2);
    }

    public void addlt(Operand dest, Operand src1, Operand src2) {
        insn("addlt", dest, src1, src2);
    }

    public void mul(Operand dest, Operand src1, Operand src2) {
        insn("mul", dest, src1, src2);
    }

    // public void sdiv(Operand dest, Operand dividend, Operand divider) {
    // insn("sdiv", dest, dividend, divider);
    // }

    public void cmp(Operand src1, Operand src2) {
        insn("cmp", src1, src2);
    }

    public void bne(Label label) {
        insn("bne", new DirectMemoryReference(label.symbol()));
    }

    public void call(Operand funcName) {
        insn("bl", funcName);
    }
    // public void mod(Operand dest, Operand dividend, Operand divider) {
    // insn("sdiv", dest, dividend, divider);
    // }

    public String toSource() {
        StringBuffer buf = new StringBuffer();
        for (Assembly asm : assemblies) {
            //System.out.println(asm.toSource(labelSymbols));
            buf.append(asm.toSource(labelSymbols));
            buf.append("\n");
        }
        return buf.toString();
    }
}